function TasksView(aspirantId, parentNodeId, actionURL){
	var _aspirantId = aspirantId;        
    var _parentNodeId = parentNodeId;
    var _table = null;
    var _curYear = null;
    var _curMonth = null;
    var _curDate = null;
    var _actionURL = actionURL;
    //var _curWeekday = null;
    var _dataColCount = 3;
    var _tasks = null;    
    var _filter = 0; // don't filter any task
    
    //var TITLE_COL_BK_COLOR = "#E78F28";
    //var LABEL_FONT_COLOR = "white";
    var WEEKDAYS = new Array(7);
    var TASK_RANK_BK_COLORS = new Array(4);
    var TASK_RANK_FONT_COLORS = new Array(4);
    var TIME_ROW_COUNT = 24;
    var SLICE_LENGTH = 5;
    var FIXED_COL_COUNT = 2;
    var DATA_COL_PREFIX = "datacol_";
    var TITLE_COL_PREFIX = "date_title_";
    //var TASK_BLOCK_PREFIX = "block_";
    var NOT_FILTERRED = 0;
    
    var _bodyColumns = new Array(TIME_ROW_COUNT);
    //var _timeSlices = new Array(TIME_ROW_COUNT);
    var _dataColumns = new Array(_dataColCount);    
    var _dataHandler = null; // handle the data    
    var _calendar;    
    
    var _messagePart;
    
    //var _selectedTaskId;
    
    {
        WEEKDAYS[0] = "周日";
        WEEKDAYS[1] = "周一";
        WEEKDAYS[2] = "周二";
        WEEKDAYS[3] = "周三";
        WEEKDAYS[4] = "周四";
        WEEKDAYS[5] = "周五";
        WEEKDAYS[6] = "周六";   
        TASK_RANK_BK_COLORS[0] = "yellow";
        TASK_RANK_BK_COLORS[1] = "blue";
        TASK_RANK_BK_COLORS[2] = "red";
        TASK_RANK_BK_COLORS[3] = "green";
        TASK_RANK_FONT_COLORS[0] = "black";
        TASK_RANK_FONT_COLORS[1] = "white";
        TASK_RANK_FONT_COLORS[2] = "white";
        TASK_RANK_FONT_COLORS[3] = "white";
        dt = new Date();
        _curYear = dt.getFullYear();
        _curMonth = dt.getMonth() + 1;
        _curDate = dt.getDate();
        // _curWeekday = dt.getDay();
        
        createTable();
        
        loadTasks(dt, nextNDate(dt, _dataColCount - 1));
    }
    
    this.setDataColCount = function(num){
        _dataColCount = num;
    }
    
    this.setCurDate = function(year, month, date, weekday){
        _curYear = year;
        _curMonth = month;
        _curDate = date;
        //_curWeekday = weekday;
    }
    
    this.setActionURL = function(url){
        _actionURL = url;
    }
    
    this.setFilter = function(filter){
    	_filter = filter;
    }
    
    this.flushFromDate = function(date){
    	loadTasks(date, nextNDate(date, _dataColCount));
    }
    
    this.flush = function(){
    	buildData(_tasks);
    }  
        
    this.setDataHandler = function(handler){
    	_dataHandler = handler;
    }
    
    this.setCalendar = function(cal){
    	_calendar = cal;
    }
    /*
    this.addTask = function(task){
        if(task == null){
            return false;
        }
        var dts = task.getScheduledStartDate().split("-");
        var col = getColumn(dts[1], dts[2], task.getStartTimeSlice());
        if(col != null){            
            col.appendChild(createTaskBlock(task));
            _tasks.push(task);
        }
        
        return true;
    }
    
    this.saveTask = function(task){
    	return true;
    }
    */
    function createTable(){
        var parent = $(_parentNodeId);
        if(parent == null){
            alert("the parent doesn't exist for this id " + _parentNodeId);
            return;
        }
        
        _table = document.createElement("table");
        parent.appendChild(_table);
        _table.cellSpacing = "0";
        _table.cellpadding = "0";
        _table.className = "tasksview";
        //_table.border = "1";
        
        _table.appendChild(createTHead());
        var titles = getNextDateTitles(_curYear, _curMonth, _curDate);        
        _table.appendChild(createTTitle(titles));
        _table.appendChild(createTBody(_dataColCount));
    }
    
    function createTHead(){
    	var head = document.createElement("tr");
        head.className = "table-head";
        var td = document.createElement("td");
        td.colSpan = _dataColCount + FIXED_COL_COUNT;
        head.appendChild(td);
        
        td.appendChild(createButton("taskcal_pre", "《", goToLastCycle));
        var span = document.createElement("span");
        td.appendChild(span);
        span.className = "date-scope";
        span.id = "taskcal_date_scope";
        td.appendChild(createButton("taskcal_nxt", "》", goToNextCycle));
        
        // create message place
        var div = document.createElement("div");        
        td.appendChild(div);
        div.className = "message";
        _messagePart = document.createElement("span");
        div.appendChild(_messagePart);
        return head;
    }
    
    function createButton(id, value, f_onclick){
		var btn = document.createElement("input");
		btn.id = id;
		btn.type = "button";
		btn.value = value;
		btn.onclick = f_onclick;
		//btn.style.background = "#B8D4E8";
		//btn.style.color = "white";
		return btn;
	}
    
    function createTTitle(titles){
        if(titles == null || titles.length < 1){
            return;
        }
        var head = document.createElement("tr");
        head.className = "table-head";
        var th = document.createElement("th");
        th.className = "table-slice";
        head.appendChild(th);   
        for(var i = 0; i < titles.length; i+=1){
            var th = document.createElement("th");
            head.appendChild(th);
            th.id = TITLE_COL_PREFIX + i;
            th.className = "col-title";
            th.innerHTML = titles[i];
        }  
        var th = document.createElement("th");        
        th.className = "scroll";
        head.appendChild(th);
        return head;
    }
    
    function createTBody(colNum){
        if(colNum < 1){
            return;
        }
        
        var tbody = document.createElement("tr");
        var td = document.createElement("td");
        td.colSpan = _dataColCount + FIXED_COL_COUNT;
        tbody.appendChild(td);
        var div = document.createElement("div");
        td.appendChild(div);
        div.className = "tbody";
        var innerTable = document.createElement("table");
        innerTable.cellSpacing = "0";
        //innerTable.border = "1";
        //innerTable.borderColor = "red";
        div.appendChild(innerTable);
        //tbody.id = "task_view_tbody";
        //tbody.className = "tbody";
        
        for(i = 0; i < TIME_ROW_COUNT; i += 1){
            var row = document.createElement("tr");    
            innerTable.appendChild(row);            
            var slc = document.createElement("td");
            row.appendChild(slc);
            slc.className = "table-slice";
            var label = (i < 10) ? "0" : "";
            slc.id = label + i + ":00";
            slc.innerHTML = slc.id;
            
            _bodyColumns[i] = new Array(colNum);
            for(j = 0; j < colNum; j += 1){
                var col = document.createElement("td");
                row.appendChild(col);
                col.id = DATA_COL_PREFIX + _dataColumns[j] + "_" + slc.id;
                col.className = "col-content";
                _bodyColumns[i][j] = col;
            }
        }
        return tbody;
    }

    function createTaskBlock(task){            
        var block = document.createElement("table");
        block.className = "task-block";
        //block.style.background = TASK_RANK_BK_COLORS[task.getRank() - 1];
        
        var tr = document.createElement("tr");
        block.appendChild(tr);
        var title = document.createElement("td");
        tr.appendChild(title);
        title.className = "title";
        title.style.color = TASK_RANK_FONT_COLORS[task.taskRank - 1];
        title.style.background = TASK_RANK_BK_COLORS[task.taskRank - 1];
        title.innerHTML = "";
        var sh = task.scheduledStartDate.getHours();
        if(sh < 10){
        	title.innerHTML += "0";
        }
        title.innerHTML += sh + ":";
        var sm = task.scheduledStartDate.getMinutes();
        if(sm < 10){
        	title.innerHTML += "0";
        }
        title.innerHTML += sm + "-";
        sh = task.scheduledEndDate.getHours();
        if(sh < 10){
        	title.innerHTML += "0";
        }
        title.innerHTML += sh + ":";
        var sm = task.scheduledEndDate.getMinutes();
        if(sm < 10){
        	title.innerHTML += "0";
        }
        title.innerHTML += sm;
        
        var tr = document.createElement("tr");
        block.appendChild(tr);        
        var content = document.createElement("td");
        tr.appendChild(content);    
        content.className = "content";
        //content.
        
        //var link = document.createElement("<a href=''></a>");
        //content.appendChild(link);
        //link.textContent = task.getContent();
        //link.style.color = TASK_RANK_FONT_COLORS[task.getRank() - 1];
        //link.href = _actionURL + task.getId();
        content.innerHTML = "<a href='" + _actionURL + task.id + "'>" + task.content + "</a>";
        /*
        block.id = TASK_BLOCK_PREFIX + task.getId();
        block.onclick = function(){
        	_selectedTaskId = this.id;
        	this.borderColor = "blue";
        }*/
        return block;
    }
     
    // load the tasks whose scheduled time is among start and its next N date
    function loadTasks(start, end){
    	if(start == null || end == null){
    		return;
    	}
    	RDC.gui.showProgressIn(_messagePart, 1);
    	// e.g if the start is 2009-10-01 and end is 2009-10-03, so it needs run query on 2009-10-01 00:00:00 and 2009-10-04 00:00:00
    	end = nextNDate(end, 1);
    	TaskHandler.getAllTasks(_aspirantId, start, end, function(results){
    		_tasks = results;
    		buildData(_tasks);
    		_messagePart.innerHTML = "";
    	});
    	// _tasks = _dataHandler.loadData();
    	//buildData(_tasks);    	
    }
       
    function goToLastCycle(){
    	var dt = new Date(_curYear, _curMonth - 1, _curDate);
    	goToNewCycle(dt, false);
    	_calendar.updateCalendarWith(_curYear, _curMonth, _curDate);
    	this.blur();
    }
    
    function goToNextCycle(){
    	var dt = new Date(_curYear, _curMonth - 1, _curDate);
    	goToNewCycle(dt, true);
    	_calendar.updateCalendarWith(_curYear, _curMonth, _curDate);
    	this.blur();
    }
    
    // if true, go to next cycle, or else go to a last one
    function goToNewCycle(date, isNext){
    	var start = null;
    	if(isNext){
    		start = nextNDate(date, _dataColCount);
    	}
    	else {
    		start = lastNDate(date, _dataColCount);
    	}
    	updateWithNewStart(start);
    }
    
    function updateWithNewStart(start){
    	if(start == null){
    		return;
    	}
    	setDateTitles(start);
    	setCurrentDate(start);
    	loadTasks(start, nextNDate(start, _dataColCount - 1));
    }
        
    function setCurrentDate(date){
    	_curYear = date.getFullYear();
    	_curMonth = date.getMonth() + 1;
    	_curDate = date.getDate();
    }
    
    this.getNewCycle = function(start){
    	updateWithNewStart(start);
    }
 
    function setDateTitles(start){
    	var titles = getNextDateTitles(start.getFullYear(), start.getMonth() + 1, start.getDate());
    	if(titles == null || titles.length != _dataColCount){
    		alert("wrong date titles");
    		return;
    	}
    	
    	for(var i = 0; i < titles.length; i+=1){
    		var col = $(TITLE_COL_PREFIX + i);
    		if(col != null){
    			col.innerHTML = titles[i];
    		}
    	}
    }
       
    function getNextDateTitles(year, month, date){
        var cur = new Date(year, month, date);
        var titles = new Array(_dataColCount);
        var curDate = new Date(year, month - 1, date); 
        var dateScope = $("taskcal_date_scope");
        if(dateScope != null) {
        	dateScope.innerHTML = "";
        }
        for(i = 0; i < _dataColCount; i += 1){
            var month = curDate.getMonth() + 1;
            var m = (month < 10) ? "0" + month : month;
            var d = (curDate.getDate() < 10) ? "0" + curDate.getDate() : curDate.getDate();
            _dataColumns[i] = m + "/" + d;
            titles[i] = _dataColumns[i] + " " + WEEKDAYS[curDate.getDay()];
            
            if(dateScope != null){
	            if(i == 0 
	            	|| (_dataColCount != 1 && i == (_dataColCount - 1))){
	            	if(i != 0){
	            		dateScope.innerHTML += "~";
	            	}
	            	dateScope.innerHTML += curDate.getFullYear() + "-" + m + "-" + d;
	            }
            }
            
            curDate = nextNDate(curDate, 1);
        }
        return titles;
    } 
 
    function lastNDate(date, N){
    	if(date == null){
    		return null;
    	}
    	if(N < 1){
    		return date;
    	}
    	var year = date.getFullYear();
        var month = date.getMonth() + 1;
        var dt = date.getDate();
        var curDate = new Date();
        for(var i = 0; i < N; i+=1){        	
	        if(dt < 1){
	        	// get the max date of next month
		        curDate.setFullYear(year);
		        curDate.setMonth(month - 1);
		        curDate.setDate(0);
		        dt = curDate.getDate();
	            year = (month == 1) ? (year - 1): year;
	            month = (month == 1) ? 12: (month - 1);             
	        }
	        else {
	            dt -= 1;
	        }
        }
        curDate.setFullYear(year);
        curDate.setMonth(month - 1);
        curDate.setDate(dt);        
        return curDate;
    }
       
    function nextNDate(date, N){
    	if(date == null){
    		return null;
    	}
    	if(N < 1){
    		return date;
    	}    	
    	var year = date.getFullYear();
        var month = date.getMonth() + 1;        
        var dt = date.getDate();
        var curDate = new Date();
        for(var i = 0; i < N; i += 1){
	        curDate.setFullYear(year);
	        curDate.setMonth(month);
	        curDate.setDate(0);
	        var max = curDate.getDate();
	        if(dt >= max){
	            dt = 1;
	            year = (month == 12) ? (year + 1): year;
	            month = (month == 12) ? 1: (month + 1);             
	        }
	        else {
	            dt += 1;
	        }
        }
        curDate.setFullYear(year);
        curDate.setMonth(month - 1);
        curDate.setDate(dt);        
        return curDate;
    }
    
    function cleanup(){
    	/*var dtable = $("inner_data_table");
    	if(dtable == null){
    		return;
    	}
    	for(var i = 0;i < dtable.all.length; i+=1){
    		var elem = dtable.all[i];
    		if(elem != null && elem.id){
    			elem.innerHTML = "";
    		}
    	}*/
    	for(var i = 0; i < _bodyColumns.length; i+=1){
    		for(var j = 0; j < _bodyColumns[i].length; j+=1){
    			_bodyColumns[i][j].innerHTML = "";
    		}
    	}
    }
 
    function buildData(tasks){
        if(tasks == null){
            return;
        }      
        cleanup();
        
        for(var i = 0; i < tasks.length; i += 1){
            var task = tasks[i];
            if(task == null || (_filter != task.taskRank && _filter != NOT_FILTERRED)){
                continue;
            }
            var col = getColumn(task.scheduledStartDate);
            if(col != null){
                col.appendChild(createTaskBlock(task));
            }
        }       
    }
    
    function getColumn(date){
        if(date == null){
            return null;
        }
         var month = date.getMonth() + 1;
        var dt = date.getDate();
        var colIndex = -1;
        for(var i = 0; i < _dataColCount; i+=1){
        	var mds = _dataColumns[i].split("/"); // split 09/11 to 09 and 11
         	if(month == mds[0]&& dt == mds[1]){
        		colIndex = i;
        	}
        }
        var rowIndex = date.getHours();
        if(colIndex >= 0 && colIndex < _dataColCount 
        	 && rowIndex >= 0 && rowIndex < TIME_ROW_COUNT){
        	 return _bodyColumns[rowIndex][colIndex];
        }
        return null;
    }


}
